/**
 *  Copyright 2007-2008 University Of Southern California
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */


package edu.isi.pegasus.common.util;

import edu.isi.pegasus.common.util.DynamicLoader;



/**
 * The base exception class to notify of errors, while instantiating classes
 * via any of the factories. Each factory, should throw an exception that is
 * a subclass of this class.
 *
 * @author Karan Vahi
 * @author Jens-S. Vöckler
 * @author Gaurang Mehta
 */
public class FactoryException
    // method A: no need to change interface, obsfuscated use, though
    extends java.lang.RuntimeException
    //method B: needs API small change, but makes things clear.
    //extends java.lang.Exception
    {

    /**
     * The default classname that is associated with the exception.
     */
    public static final String DEFAULT_NAME = "Object";

    /**
     * The name of the class that was trying to be instantiated when this
     * error occured, or some other signifier like module name.
     */
    protected String mClassname;

    /**
     * Converts most of the common instantiation exceptions from the class
     * loader into an error message. Use for getting sensible error messages,
     * of the causes that are associated with a FactoryException. The index
     * in the messages starts from 0.
     *
     * @return the error message
     */
    public String convertException(){
        return convertException( mClassname, this, 0 );
    }

    /**
     * Converts most of the common instantiation exceptions from the class
     * loader into an error message. Use for getting sensible error messages,
     * of the causes that are associated with a FactoryException. The index
     * in the messages starts from 0.
     *
     * @param index     the index to start from.
     *
     * @return  the error message.
     */
    public  String convertException( int index ){
        return convertException( mClassname, this , index );
    }



    /**
     * Converts most of the common instantiation exceptions from the class
     * loader into an error message. Use for getting sensible error messages,
     * of the causes that are associated with a FactoryException.
     *
     * @param classname the class that was trying to be loaded or some other
     *                  signifier.
     * @param e         the FactoryException that is thrown.
     * @param index     the index to start from.
     *
     * @return  the error message.
     */
    public static String convertException( String classname, Throwable e , int index){
        Throwable prev = null;
        StringBuffer message = new StringBuffer();

        int i = index;
        //append all the causes
        for(Throwable cause = e; cause != null ; cause  = cause.getCause()){
            message.append("\n [").append(Integer.toString(++i) ).append(  "]: ");
            if ( cause instanceof FactoryException ){
                message.append( cause.getMessage() );
                classname = ((FactoryException)cause).getClassname();
            }
            else if ( prev != null && prev instanceof FactoryException ){
                //we can use the convert method
                message.append( DynamicLoader.convertExceptionToString( classname, cause ) );
            }
            else{
                //for all other exceptions just chain the message
                message.append( cause.getMessage() );
            }

            //append just one elment of stack trace for each exception
            message.append( " at " ).append( cause.getStackTrace()[0] );

            prev = cause;
        }
        return message.toString();
    }


    /**
     * Constructs a <code>FactoryException</code> with no detail
     * message. The associated classname is set to value specified by
     * <code>DEFAULT_NAME</code>.
     *
     * @param msg the detailed message.
     *
     * @see #DEFAULT_NAME
     */
    public FactoryException( String msg ) {
        super( msg );
        mClassname = this.DEFAULT_NAME;
    }

    /**
     * Constructs a <code>FactoryException</code> with the specified detailed
     * message.
     *
     * @param msg       is the detailed message.
     * @param classname the name of class that was trying to be instantiated or
     *                  some other signifier like module name.
     */
    public FactoryException(String msg, String classname) {
        super( msg );
        mClassname = classname;
    }


    /**
     * Constructs a <code>FactoryException</code> with the
     * specified detailed message and a cause. The associated classname is set
     * to value specified by <code>DEFAULT_NAME</code>.
     *
     * @param msg       is the detailed message that is to be logged.
     * @param cause is the cause (which is saved for later retrieval by the
     * {@link java.lang.Throwable#getCause()} method). A <code>null</code>
     * value is permitted, and indicates that the cause is nonexistent or
     * unknown.
     *
     * @see #DEFAULT_NAME
     */
    public FactoryException(String msg, Throwable cause) {
        super(msg, cause);
        mClassname = this.DEFAULT_NAME;
    }


    /**
     * Constructs a <code>FactoryException</code> with the
     * specified detailed message and a cause.
     *
     * @param msg       is the detailed message that is to be logged.
     * @param classname the name of class that was trying to be instantiated.
     * @param cause is the cause (which is saved for later retrieval by the
     * {@link java.lang.Throwable#getCause()} method). A <code>null</code>
     * value is permitted, and indicates that the cause is nonexistent or
     * unknown.
     */
    public FactoryException(String msg, String classname, Throwable cause) {
        super(msg, cause);
        mClassname = classname;
    }

    /**
     * Returns the name of the class that was trying to be loaded when this
     * error occured, or some other signifier like a module name.
     *
     * @return the name of the class.
     */
    public String getClassname(){
        return this.mClassname;
    }

}
